//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The core libraries for the MapWindow 6.0 project.
//
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in January, 2008.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using MapWindow.Data;
using MapWindow.Main;
using MapWindow.Geometries;
namespace MapWindow.Drawing
{
    


    /// <summary>
    /// This is the specific surface type where code for handling the integer grids 
    /// </summary>
    public class GridBottom : IRenderable
    {
        /// <summary>
        /// Occurs immediately after the visible parameter has been adjusted.
        /// </summary>
        public event EventHandler VisibleChanged;

        #region Private Variables

        private IndexBuffer _indexBuffer;
        private VertexBuffer _vertexBuffer;
        private Device _device;
       
        private IEnvelope _extents;
        private int[] _indices;
        private bool _isLoaded;
        private Color _color;
        private bool _isVisible;

        #endregion

        #region constructors

        /// <summary>
        /// Constructs a new flat, gray grid bottom at 0 elevation.
        /// </summary>
        /// <param name="device">A Direct3D Device.</param>
        /// <param name="extents">Any valid IEnvelope that defines where the butotm should exist.</param>
        public GridBottom(IEnvelope extents, Device device)
            : this(extents, Color.Gray, 0f, device)
        {
            // This just forwards the default null ProgressHandler
        }


        /// <summary>
        /// Constructs a new flat, grid bottom with the specified color at 0 elevation.
        /// </summary>
        /// <param name="device">A Direct3D Device.</param>
        /// <param name="extents">Any valid IEnvelope that defines where the butotm should exist.</param>
        /// <param name="color">A System.Drawing.Color to use for creating the grid bottom.</param>
        public GridBottom(IEnvelope extents, Color color, Device device)
            : this(extents, color, 0f, device)
        {
            // This just forwards the default null ProgressHandler
        }

        /// <summary>
        /// Constructs a new flat, gray grid bottom at the specified elevation.
        /// </summary>
        /// <param name="device">A Direct3D Device.</param>
        /// <param name="extents">Any valid IEnvelope that defines where the butotm should exist.</param>
        /// <param name="elevation">The float elevation that the bottom should be drawn at.</param>
        public GridBottom(IEnvelope extents, float elevation, Device device)
            : this(extents, Color.Gray, elevation, device)
        {
            // This just forwards the default null ProgressHandler
        }


        /// <summary>
        /// Constructs a new flat, grid bottom with the specified color at the specified elevation.
        /// </summary>
        /// <param name="device">A Direct3D Device.</param>
        /// <param name="extents">Any valid IEnvelope that defines where the butotm should exist.</param>
        /// <param name="color">A System.Drawing.Color to use for creating the grid bottom.</param>
        /// <param name="elevation">The float elevation that the bottom should be drawn at.</param>
        public GridBottom(IEnvelope extents, Color color, float elevation, Device device)
        {

            _device = device;
            _extents = extents;
            _color = color;
            InitializeIndices();

            // create VertexBuffer to store the points based on colors
            _vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), 4, _device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default);
            _vertexBuffer.Created += new EventHandler(this.OnVertexBufferCreate);
            OnVertexBufferCreate(_vertexBuffer, null);

            // create IndexBuffer to store indices into vertex data
            _indexBuffer = new IndexBuffer(typeof(int), 6, _device, Usage.WriteOnly, Pool.Default);
            _indexBuffer.Created += new EventHandler(this.OnIndexBufferCreate);
            OnIndexBufferCreate(_indexBuffer, null);
            
            _isLoaded = true;
        }

        #endregion

        /// <summary>
        /// If this is false, then the drawing function will not render anything.
        /// Warning!  This will also prevent any execution of calculations that take place
        /// as part of the drawing methods and will also abort the drawing methods of any
        /// sub-members to this IRenderable.
        /// </summary>
        public bool IsVisible
        {
            get { return _isVisible; }
            set { _isVisible = value; }
        }


        /// <summary>
        /// Boolean value that indicates whether or not values have been organized to form this grid.
        /// </summary>
        public bool IsLoaded
        {
            get { return _isLoaded; }
            set { _isLoaded = value; }
        }

        /// <summary>
        /// Gets the envelope for this raster.
        /// </summary>
        public IEnvelope Envelope
        {
            get
            {
                return _extents;
            }
        }

        private void InitializeIndices()
        {
            // Organize the 6 index values along these lines.  This ensures both are clockwise, and so respond the same to partial rendering.
            //
            //  0  3 --4           Vertex 0        Vertex 1
            //  |\  \  |
            //  | \  \ |
            //  |  \  \|
            //  2-- 1  5           Vertex 2        Vertex 3
            //

            _indices = new int[6];
            _indices[0] = 0;
            _indices[1] = 3;
            _indices[2] = 2;
            _indices[3] = 0;
            _indices[4] = 1;
            _indices[5] = 3;
         
        }

        /// <summary>
        /// Populate the vertex buffer with the integer elevations stored in the file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnVertexBufferCreate(object sender, EventArgs e)
        {
            //  If we have no data, we can't create the vertex buffer yet.
            if (_extents == null) return;
            CustomVertex.PositionColored[] vertsC = new CustomVertex.PositionColored[4];
            VertexBuffer buffer = (VertexBuffer)sender;
            float top = System.Convert.ToSingle(_extents.MaxY);
            float bottom = System.Convert.ToSingle(_extents.MinY);
            float left = System.Convert.ToSingle(_extents.MinX);
            float right = System.Convert.ToSingle(_extents.MaxX);

            vertsC[0] = new CustomVertex.PositionColored(left, top, 0f, _color.ToArgb());
            vertsC[1] = new CustomVertex.PositionColored(right, top, 0f, _color.ToArgb());
            vertsC[2] = new CustomVertex.PositionColored(left, bottom, 0f, _color.ToArgb());
            vertsC[3] = new CustomVertex.PositionColored(right, bottom, 0f, _color.ToArgb());

            buffer.SetData(vertsC, 0, LockFlags.None);
        }

        private void OnIndexBufferCreate(object sender, EventArgs e)
        {
            _indexBuffer.SetData(_indices, 0, LockFlags.None);
        }

       


        /// <summary>
        /// Allows this item to render itself to a 3D device
        /// </summary>
        /// <param name="device">The specified Microsoft.DirectX.Direct3D.Device for this object to draw itself on</param>
        public void Draw(IGraphics device)
        {
            Draw(device, null);
        }

        /// <summary>
        /// Allows this item to render itself to a 3D device
        /// </summary>
        /// <param name="device">The specified Microsoft.DirectX.Direct3D.Device for this object to draw itself on</param>
        /// <param name="progressHandler">Any valid implementation of the MapWindow.Main.IProgressHandler interface to receive progress messages</param>
        public void Draw(IGraphics device, IProgressHandler progressHandler)
        {
            
            if (device == null)
            {
                OnAfterDrawing(device, false, new ArgumentException("device was null or could not be cast to a Microsoft.DirectX.Direct3D.Device"));
                return;
            }
            if (_isLoaded)
            { 
                device.VertexFormat = CustomVertex.PositionColored.Format;
                device.SetStreamSource(0, _vertexBuffer, 0);
                device.Indices = _indexBuffer;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, _indices.Length / 3);   
            }
        }

        /// <summary>
        /// Allows the object to dispose the unmannaged memory components.
        /// </summary>
        public void Dispose()
        {
            //_Texture.Dispose();
            _indexBuffer.Dispose();
            _vertexBuffer.Dispose();
        }
        #region Draw Events

        /// <summary>
        /// Occurs when the Draw method is called and provides the option to cancel the event before anything is actually drawn.
        /// </summary>
        public virtual event EventHandler<DrawCancel> BeforeDrawing;

        /// <summary>
        /// Fires the event that occurs before drawing to a directX map
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that will be drawn to</param>
        /// <returns>Boolean, that if true should cancel the draw event.</returns>
        protected virtual bool OnBeforeDrawing(object device)
        {
            if (BeforeDrawing == null) return false;
            DrawCancel e = new DrawCancel(device);
            BeforeDrawing(this, e);
            return e.Cancel;
        }


        /// <summary>
        /// Occurs after the contents of this MapLayerCollection have been rendered to the Microsoft.DirectX.Direct3D.Device
        /// </summary>
        public virtual event EventHandler<DrawCompleted> AfterDrawing;

        /// <summary>
        /// Fires an event when we are done with the drawing code, whether the drawing was successful, cancelled, or threw an exception.
        /// </summary>
        /// <param name="device">A DirectX.Direct3D.Device that rendering took place on</param>
        /// <param name="cancelled">If true, the event was cancelled normally</param>
        /// <param name="ex">An Exception in the event that this was an error.  This will normally be null.</param>
        protected virtual void OnAfterDrawing(object device, bool cancelled, Exception ex)
        {
            if (AfterDrawing == null) return;
            DrawCompleted e;
            if (ex == null)
            {
                e = new DrawCompleted(device, cancelled);
            }
            else
            {
                e = new DrawCompleted(device, ex);
            }
            AfterDrawing(this, e);
        }


        #endregion

        /// <summary>
        /// Fires the Visible Changed event
        /// </summary>
        protected virtual void OnVisibleChanged()
        {
            VisibleChanged(this, new EventArgs());
        }

    }


}
